#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
#include <string>
#include <mutex>
#include <vector>
#include "Mythread.hpp"
#include "Lockgarud.hpp"

using namespace Mythread;

class ThreadData
{
public:
	ThreadData(int &tickets, std::string &name, pthread_mutex_t &mutex)
		: _tickets(tickets), _name(name), _total(0), _mutex(mutex)
	{}

	~ThreadData()
	{}

public:
	int &_tickets;
	std::string _name;
	int _total;
	// std::mutex &_mutex;
	pthread_mutex_t &_mutex;
};

// 加锁方式:
// 1、全局的，pthread_mutex_t gmutex = PTHREAD_MUTEX_INITIALIZER;定义完这个后直接用
//	 pthread_mutex_lock和pthread_mutex_unlock进行加锁就行。
// 2、创建局部对象pthread_mutex_t，用pthread_mutex_init初始化,然后传参用
//  	 pthread_mutex_lock和pthread_mutex_unlock进行加锁就行。
// 3、C++封装传对象
// 4、RAII	临时对象， RAII风格的加锁和解锁

// pthread_mutex_t gmutex = PTHREAD_MUTEX_INITIALIZER;

int g_tickets = 10000; // 共享资源，没有保护的


// 访问临界资源的代码，叫做临界区！--- 我们加锁，本质就是把并行执行 --- 串行执行 --- 加锁的粒度要约细越好
// 加锁 : 竞争锁是自由竞争的，竞争锁的能力太强的线程，会导致其他线程抢不到锁 --- 造成了其他线程的饥饿问题！
void route(ThreadData *td)
{
	while (true)
	{
		// pthread_mutex_lock(&gmutex);
		// td->_mutex.lock();
		// pthread_mutex_lock(&td->_mutex);

		LockGuard guard(&td->_mutex);		//会自己析构解锁
		if (td->_tickets > 0)
		{
			usleep(1000);
			printf("%s running , get tickets: %d\n", td->_name.c_str(), td->_tickets);
			td->_tickets--;
			// pthread_mutex_unlock(&gmutex);
			// td->_mutex.unlock();
			// pthread_mutex_unlock(&td->_mutex);
			td->_total++;
		}
		else
		{
			// pthread_mutex_unlock(&gmutex);
			// td->_mutex.unlock();
			// pthread_mutex_unlock(&td->_mutex);
			break;
		}
	}
}

const int num = 5;
int main()
{
	// 创建一批线程
	std::vector<Thread<ThreadData *>> threads;
	std::vector<ThreadData *> Datas;

	pthread_mutex_t mutex;
	pthread_mutex_init(&mutex, nullptr);

	// std::mutex mutex;

	for (int i = 0; i < num; i++)
	{
		std::string tdname = "thread-" + std::to_string(i + 1);
		ThreadData *td = new ThreadData(g_tickets, tdname, mutex);
		threads.emplace_back(route, td, tdname);
		Datas.emplace_back(td);
	}

	// 启动线程
	for (auto &td : threads)
	{
		td.start();
	}

	// 等待线程
	for (auto &td : threads)
	{
		td.join();
		std::cout << "wait thread done, thread is: " << td.name() << std::endl;
	}

	// 输出结果
	for (auto ret : Datas)
	{
		std::cout << ret->_name << " : " << ret->_total << std::endl;
		delete ret;
	}
	return 0;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////

// __thread int g_val = 6;		//__thread,让线程之间访问不同的资源，各自访问各自的

// // // 线程是可以分离的: 默认线程是joinable的。
// // // 如果我们main thread不关心新线程的执行信息，我们可以将新线程设置为分离状态:
// // // 你是如何理解线程分离的呢？底层依旧属于同一个进程！只是不需要等待了
// // // 一般都希望mainthread 是最后一个退出的，无论是否是join、detach
// void *threadrun1(void *args)
// {
// 	std::string name = static_cast<const char *>(args);
// 	while (g_val)
// 	{
// 		sleep(1);
// 		std::cout << name << "g_val: " << g_val << std::endl;
// 		g_val--;
// 	}

// 	return nullptr;
// }

// void *threadrun2(void *args)
// {
// 	std::string name = static_cast<const char *>(args);
// 	while (g_val)
// 	{
// 		sleep(1);
// 		std::cout << name << "g_val: " << g_val << std::endl;
// 	}

// 	return nullptr;
// }

// int main()
// {
// 	pthread_t t1;
// 	pthread_t t2;
// 	pthread_create(&t1, nullptr, threadrun1, (void *)"thread-1");
// 	pthread_create(&t1, nullptr, threadrun2, (void *)"thread-2");

// 	pthread_detach(t1);

// 	pthread_join(t1, nullptr);
// 	pthread_join(t2, nullptr);

// 	return 0;
// }

////////////////////////////////////////////////////////////////////////////////////////////////////////////

// C++11中的线程创建
// 在用g++编译时还是需要带-pthread选项，因为底层还是用到了原生线程库，只是对它进行了封装
//  #include <iostream>
//  #include <thread>
//  #include <unistd.h>

// void threadrun(int num)
// {
//     while(num)
//     {
//         std::cout << "I am a thread, num: " << num << std::endl;
// 		num--;
//         sleep(1);
//     }
// }

// int main()
// {
// 	std::thread td1(threadrun,10);
// 	while(true)
//     {
//         std::cout << "I am a main thread "<< std::endl;
//         sleep(1);
//     }

// 	td1.join();
// 	return 0;
// }

//////////////////////////////////////////////////////////////////////////////////////////////////////////

// #include <iostream>
// #include <pthread.h>
// #include <unistd.h>

// //创建的线程pid和进程一样，就是同一个进程，线程也可以称为轻量级进程
// //线程退出也需要主线程进程等待回收
// //如果线程异常退出了，那么整个进程也会跟着退出出异常了
// //--- 重点 --- 多线程中，任何一个线程出现异常(div 0， 野指针), 都会导致整个进程退出！ ---- 多线程代码往往健壮性不好
// //多线程最主要的优点就是共享
// // 同一个进程内的线程，大部分资源都是共享的. 地址空间是共享的！
// int g_val = 100;
// void* handler(void* args)
// {
// 	int cnt = 3;
// 	 while (cnt)
//     {
//         std::cout << "I am new thread, pid: " << getpid() << std::endl;
//         sleep(1);
// 		cnt--;
// 		g_val++;
//     }

// 	//线程函数的退出
// 	//1、return
// 	//2、pthread_exit();
// 	//不能用exit()退出，这样整个进程都会终止退出
// 	pthread_exit(nullptr);
// }

// // // 主线程退出 == 进程退出 == 所有线程都要退出
// // // 1. 往往我们需要main thread最后结束
// // // 2. 线程也要被"wait", 要不然会产生类似进程哪里的内存泄漏的问题
// int main()
// {
// 	//创建线程	pthread_create(pthread_t *thread, const pthread_attr_t *attr,
// 	//			void *(*start_routine) (void *), void *arg)
// 	pthread_t tid;
// 	pthread_create(&tid,nullptr,handler,nullptr);

// 	//2. 新和主两个线程，谁先运行呢？不确定，由调度器决定
// 	//     // 在主线程中，你保证新线程已经启动
//     sleep(2);
//     //pthread_cancel(tid);		//向线程发送取消请求

// 	int cnt = 5;
// 	while(cnt)
// 	{
// 		std::cout << "I am main thread, pid: " << getpid() << ", g_val: " << g_val << std::endl;
//         sleep(1);
// 		cnt--;
// 	}

// 	//线程等待 int pthread_join(pthread_t thread, void **retval);
// 	//retval是线程退出的返回
// 	int n = pthread_join(tid,nullptr);
// 	if(n == 0)
// 	{
// 		std::cout<<"wait pthread success..."<<std::endl;
// 	}
// 	return 0;
// }